/*
   Copyright 2009 Abdulla Abdurakhmanov (abdulla@latestbit.com)
   Original sources are available at www.latestbit.com

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.
*/

package org.latestbit.smartobjects.smartidlc.model;

import java.math.BigInteger;
import java.util.Collection;

public class IDLPrimitiveType {
	public enum TypeId {
		NULL(0),
		BYTE(1, false, 1),
		UNSIGNED_BYTE(1, true, 2),
		INT16(2, false, 3), INT32(4, false, 4), INT64(8, false, 5),
		UINT16(2, true, 6), UINT32(4, true, 7), UINT64(8, true, 8),
		FLOAT(4,false, 9), DOUBLE(8,false, 10),
		BIGINTEGER(11), BIGDECIMAL(12),
		CHAR(1,false, 13), WCHAR(2, false, 14),
		STRING(15), WSTRING(16),
		BOOLEAN(1,false, 17),
		DATETIME(18);
		
		private int sizeInBytes = 0, typeIdValue;
		private boolean unsignedType = false;
		
		TypeId( int typeIdValue ) {
			this.typeIdValue = typeIdValue;
		}
		
		TypeId(int sizeInBytes, boolean isUnsigned, int typeIdValue) { 
			this.sizeInBytes = sizeInBytes ; 
			this.unsignedType = isUnsigned; 
			this.typeIdValue = typeIdValue;
		}		

		public int getSizeInBytes() {
			return sizeInBytes;
		}

		public boolean isUnsigned() {
			return unsignedType;
		}
		
		public int getTypeIdValue() {
			return this.typeIdValue;
		}
		
	}
	
	private TypeId typeId;
	
	private Collection< IDLPritiveTypeNumberRange<String> > rangeConstraints;
	
	private Collection< String > stringMaskConstraints;
	
	private IDLPritiveTypeNumberRange <Integer> stringSizeConstraint;
	
	private int bigDecimalMaxPrecision = 8;
	

	public void setRangeConstraints(Collection< IDLPritiveTypeNumberRange<String> > rangeConstraints) {
		this.rangeConstraints = rangeConstraints;
	}

	public Collection< IDLPritiveTypeNumberRange<String> > getRangeConstraints() {
		return rangeConstraints;
	}

	public void setStringMaskConstraints(Collection< String > stringMaskConstraints) {
		this.stringMaskConstraints = stringMaskConstraints;
	}

	public Collection< String > getStringMaskConstraints() {
		return stringMaskConstraints;
	}

	public void setStringSizeConstraint(IDLPritiveTypeNumberRange <Integer> stringSizeConstraint) {
		this.stringSizeConstraint = stringSizeConstraint;
	}

	public IDLPritiveTypeNumberRange <Integer> getStringSizeConstraint() {
		return stringSizeConstraint;
	}

	public void setTypeId(TypeId typeId) {
		this.typeId = typeId;
	}

	public TypeId getTypeId() {
		return typeId;
	}
	
	public void setBigDecimalMaxPrecision(int bigDecimalMaxPrecision) {
		this.bigDecimalMaxPrecision = bigDecimalMaxPrecision;
	}

	public int getBigDecimalMaxPrecision() {
		return bigDecimalMaxPrecision;
	}	
	
	public boolean hasConstraints() {
		if (rangeConstraints!=null || stringSizeConstraint!= null || stringMaskConstraints!=null )
			return true;
		else
			return false;
	}
	
	public void setupIntTypeByConstraints() {
		BigInteger minInRange = BigInteger.ZERO, maxInRange = BigInteger.ZERO;
		for ( IDLPritiveTypeNumberRange<String> range : getRangeConstraints() ) {	
			if( new BigInteger(range.getMin()).compareTo( minInRange ) < 0 ) {
				minInRange = new BigInteger(range.getMin());
			}
			if( new BigInteger(range.getMax()).compareTo( maxInRange ) > 0 ) {
				maxInRange = new BigInteger(range.getMax());
			}
		}
		
		if ( minInRange.compareTo(BigInteger.ZERO) < 0 
				&& maxInRange.compareTo(BigInteger.ZERO) > 0 ) {
			maxInRange = maxInRange.negate();
		}
		if ( minInRange.compareTo(BigInteger.ZERO) > 0 
				&& maxInRange.compareTo(BigInteger.ZERO) < 0 ) {
			minInRange = minInRange.negate();
		}
		
		TypeId minTypeId = getTypeIdForNumber(minInRange);
		TypeId maxTypeId = getTypeIdForNumber(maxInRange);
		
		if(minTypeId == TypeId.BIGINTEGER || maxTypeId == TypeId.BIGINTEGER)
			setTypeId(TypeId.BIGINTEGER);
		else {
			if ( maxTypeId.getSizeInBytes() > minTypeId.getSizeInBytes() )
				setTypeId ( maxTypeId );
			else
				setTypeId ( minTypeId );
		}
	}

	private TypeId getTypeIdForNumber(BigInteger value) {
		TypeId result = TypeId.BYTE;
		if(value.compareTo(BigInteger.ZERO) < 0) {
			if (value.compareTo(new BigInteger("-128")) < 0)
				result = TypeId.INT16;			
			if (value.compareTo(new BigInteger("-32768")) < 0)
				result = TypeId.INT32;			
			if (value.compareTo(new BigInteger("-2147483648")) < 0)
				result = TypeId.INT64;
			if (value.compareTo(new BigInteger(String.valueOf(Long.MIN_VALUE))) < 0)
				result = TypeId.BIGINTEGER;
		}
		else {
			result = TypeId.UNSIGNED_BYTE;
			if (value.compareTo(new BigInteger("255")) > 0)
				result = TypeId.UINT16;
			if (value.compareTo(new BigInteger("65535")) > 0)
				result = TypeId.UINT32;
			if (value.compareTo(new BigInteger("4294967295")) > 0)
				result = TypeId.UINT64;
			if (value.compareTo(new BigInteger(String.valueOf(Long.MAX_VALUE))) > 0)
				result = TypeId.BIGINTEGER;
		}
		return result;
	}


	
}
